/* Copyright (C) 2021 Rain */

/* This file is part of XNIX. */

/* 
  XNIX is free software: you can redistribute it and/or modify 
  it under the terms of the GNU General Public License as published by 
  the Free Software Foundation, either version 3 of the License, or 
  (at your option) and later version. 
*/

/*
  XNIX is distributed in the hope that it will be useful, 
  but WITHOUT ANY WARRANTY; without even the implied warranty of 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
  GNU General Public License for more details. 
*/

/*
  You should have received a copy of the GNU General Public License 
   along with XNIX. If not, see <https://www.gnu.org/licenses/>.  
*/




#include <kernel/init.h>
#include <kernel/errno.h>
#include <kernel/types.h>

#include <kernel/int.h>
#include <kernel/mm.h>
#include <kernel/console.h>
#include <kernel/panic.h>

#include <arch/x86/int.h>
#include <arch/io.h>
#include <arch/sys.h>


int int_init(void) {
	/* init PIC */

	/* ICW: initial control word */
	outb(0x11, PIC0_ICW1);
	outb(0x20, PIC0_ICW2);
	outb(0x04, PIC0_ICW3);
	outb(0x01, PIC0_ICW4);

	outb(0x11, PIC1_ICW1);
	outb(0x28, PIC1_ICW2);
	outb(0x02, PIC1_ICW3);
	outb(0x01, PIC1_ICW4);

	/* disable all IRQs */

	outb(0xff, PIC0_IMR);
	outb(0xff, PIC1_IMR);

	set_trap_gate(idt_table + 0x00, 0, &divide_error);
	set_trap_gate(idt_table + 0x01, 0, &debug);
	set_trap_gate(idt_table + 0x02, 0, &nmi);
	set_trap_gate(idt_table + 0x03, 0, &int3);
	set_trap_gate(idt_table + 0x04, 0, &overflow);
	set_trap_gate(idt_table + 0x05, 0, &bound);
	set_trap_gate(idt_table + 0x06, 0, &invalid_code);
	set_trap_gate(idt_table + 0x07, 0, &device_not_avaliable);
	set_trap_gate(idt_table + 0x08, 0, &double_fault);
	set_trap_gate(idt_table + 0x09, 0, &fpu_segment_overrun);
	set_trap_gate(idt_table + 0x0a, 0, &invalid_tss);
	set_trap_gate(idt_table + 0x0b, 0, &segment_not_present);
	set_trap_gate(idt_table + 0x0c, 0, &stack_error);
	set_trap_gate(idt_table + 0x0d, 0, &general_protection);
	set_trap_gate(idt_table + 0x0e, 0, &page_fault);
	set_trap_gate(idt_table + 0x0f, 0, &reserved);
	set_trap_gate(idt_table + 0x10, 0, &fpu_error);
	set_trap_gate(idt_table + 0x11, 0, &alignment);
	set_trap_gate(idt_table + 0x12, 0, &machine);
	set_trap_gate(idt_table + 0x13, 0, &simd);
	set_trap_gate(idt_table + 0x14, 0, &virtualization);

	/* initial PIT timer */
	outb(0x34, 0x43);

	/* real time = 1.1932HZ / this */
	/* about 10ms */

#define TIMER_HZ_LOW  0x9c
#define TIMER_HZ_HIGH 0x2e

	outb(TIMER_HZ_LOW, 0x40);
	outb(TIMER_HZ_HIGH, 0x40);

	register_irq(0x00, irq0x20_interrupt, do_timer);
	register_irq(0x01, irq0x21_interrupt, do_keyboard);
	register_irq(0x02, irq0x22_interrupt, NULL);
	register_irq(0x03, irq0x23_interrupt, NULL);
	register_irq(0x04, irq0x24_interrupt, NULL);
	register_irq(0x05, irq0x25_interrupt, NULL);
	register_irq(0x06, irq0x26_interrupt, NULL);
	register_irq(0x07, irq0x27_interrupt, NULL);
	register_irq(0x08, irq0x28_interrupt, NULL);
	register_irq(0x09, irq0x29_interrupt, NULL);
	register_irq(0x0a, irq0x2a_interrupt, NULL);
	register_irq(0x0b, irq0x2b_interrupt, NULL);
	register_irq(0x0c, irq0x2c_interrupt, NULL);
	register_irq(0x0d, irq0x2d_interrupt, NULL);
	register_irq(0x0e, irq0x2e_interrupt, NULL);
	register_irq(0x0f, irq0x2f_interrupt, NULL);

	set_sys_gate(idt_table + 0x80, 1, &syscall);

	return 0;
}


void set_gate(struct idt_entry *entry, char attr, char ist, void *addr) {
#define handler ((__uint64_t) addr)

	entry->selector = KERNEL_CS;
	entry->ist = ist;
	entry->attr = attr;

	entry->off_0_16 = handler & 0xffff;
	entry->off_16_32 = handler >> 16;
	entry->off_32_64 = handler >> 32;
	entry->zero = 0x00000000;

	return;
}


void die(const char *s, const __uint64_t *rsp, char nr) {
	printk(red, "int #%b: %s. \n", nr, s);

	printk(red, "rsp now: %q\n", rsp);

/*	printk(red, "rax\t\t\trcx\t\t\trdx\n");
	printk(red, "%q\t%q\t%q\n", rsp[off_rax], rsp[off_rcx], rsp[off_rdx]);

	printk(red, "rbx\t\t\trbp\t\t\trsp\n");
	printk(red, "%q\t%q\t%q\n", rsp[off_rbx], rsp[off_rbp], rsp[off_rsp]);

	printk(red, "rsi\t\t\trdi\t\t\tr8\n");
	printk(red, "%q\t%q\t%q\n", rsp[off_rsi], rsp[off_rdi], rsp[off_r8]);

	printk(red, "r9\t\t\tr10\t\t\tr11\n");
	printk(red, "%q\t%q\t%q\n", rsp[off_r9], rsp[off_r10], rsp[off_r11]);
	
	printk(red, "r12\t\t\tr13\t\t\tr14\n");
	printk(red, "%q\t%q\t%q\n", rsp[off_r12], rsp[off_r13], rsp[off_r14]);

	printk(red, "r15\t\t\trip\t\t\trflags\n");
	printk(red, "%q\t%q\t%q\n", rsp[off_r15], rsp[off_rip], rsp[off_rflags]);

	printk(red, "ds\t\t\tss\n");
	printk(red, "%w\t%w\n", rsp[off_ds] & 0xffff, rsp[off_ss] & 0xffff);	
*/

	printk(red, "rip: %q  instruction: %q\n", rsp[off_rip], *(__uint64_t *) rsp[off_rip]);
	printk(red, "ds: %w\n", rsp[off_ds] & 0xffff);

	trace_mm();
	trace_irq();
	trace_syslog();

	for (;;) hlt();

	return;
}


void do_divide_error(const __uint64_t *rsp, __uint64_t errcode) {
	die("divide error", rsp, 0);

	return;
}


void do_debug(const __uint64_t *rsp, __uint64_t errcode) {
	die("debug", rsp, 1);

	return;
}

void do_nmi(const __uint64_t *rsp, __uint64_t errcode) {
	die("nmi", rsp, 2);

	return;
}

void do_int3(const __uint64_t *rsp, __uint64_t errcode) {
	die("int3", rsp, 3);

	return;
}

void do_overflow(const __uint64_t *rsp, __uint64_t errcode) {
	die("overflow", rsp, 4);

	return;
}

void do_bound(const __uint64_t *rsp, __uint64_t errcode) {
	die("bound", rsp, 5);

	return;
}

void do_ud(const __uint64_t *rsp, __uint64_t errcode) {
	die("undefined instruction", rsp, 6);

	return;
}

void do_no_fpu(const __uint64_t *rsp, __uint64_t errcode) {
	die("fpu not present", rsp, 7);

	return;
}

void do_double_fault(const __uint64_t *rsp, __uint64_t errcode) {
	die("double fault", rsp, 8);

	return;
}

void do_fpu_overrun(const __uint64_t *rsp, __uint64_t errcode) {
	die("fpu overrun", rsp, 9);

	return;
}

void do_invalid_tss(const __uint64_t *rsp, __uint64_t errcode) {
	die("invalid tss", rsp, 10);

	return;
}

void do_no_segment(const __uint64_t *rsp, __uint64_t errcode) {
	die("no such segment", rsp, 11);

	return;
}


void do_stack(const __uint64_t *rsp, __uint64_t errcode) {
	die("stack error", rsp, 12);

	return;
}


void do_gp(const __uint64_t *rsp, __uint64_t errcode) {
	die("general protection", rsp, 13);

	return;
}


void do_page_fault(const __uint64_t *rsp, __uint64_t errcode) {
	printk(red, "error code: %q\n", errcode);

	if (errcode & 1) 
		print(red, "page protection. \n");
	else 
		print(red, "page not exists. \n");

	if (errcode & 2) 
		print(red, "wr ");
	else 
		print(red, "rd ");

	if (errcode & 4) 
		print(red, "u ");
	else 
		print(red, "k ");

	if (errcode & 8) 
		print(red, "rsvd set ");

	if (errcode & 16) 
		print(red, "instruction");

	putc('\n', red);

	die("page fault", rsp, 14);

	return;
}


void do_reserved(const __uint64_t *rsp, __uint64_t errcode) {
	die("reserved exception", rsp, 15);

	return;
}


void do_fpu_error(const __uint64_t *rsp, __uint64_t errcode) {
	die("fpu error", rsp, 16);

	return;
}


void do_alignment(const __uint64_t *rsp, __uint64_t errcode) {
	die("alignment error", rsp, 17);

	return;
}


void do_machine(const __uint64_t *rsp, __uint64_t errcode) {
	die("machine check", rsp, 18);

	return;
}


void do_simd(const __uint64_t *rsp, __uint64_t errcode) {
	die("simd error", rsp, 19);

	return;
}


void do_virtualization(const __uint64_t *rsp, __uint64_t errcode) {
	die("virtualization error", rsp, 20);

	return;
}

